/* ----------------------------------------------------------------------
 *	Shadow Quest
 *	Copyright (C) 2009 Prageeth Silva <prageeth@thenewcoders.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *  ----------------------------------------------------------------------
 */
package sq;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import map.Item;
import map.Lightning;
import map.OtherObject;
import map.Weather;
import map.World;
import menu.Menu;
import menu.MenuTab;
import menu.SubMenu;
import menu.tabobject.MenuTabAnimation;
import menu.tabobject.MenuTabImage;
import menu.tabobject.MenuTabLabel;
import menu.tabobject.MenuTabObject;
import menu.tabobject.MenuTabSlider;
import menu.tabobject.MenuTabObject.Alignment;
import menu.tabobject.MenuTabSlider.Link;

import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import sq.game.Quest;
import units.Player;
import units.Unit;
import units.allies.Ally;
import units.villagers.Villager;

/** XML handling/parsing used to store and load data from XML files.
 *
 */
public class FileIO {

    // private static constant strings used for XML tagging
    private static final String S_GAME = "game";
    private static final String S_WORLD = "world";
    private static final String S_WEATHER = "weather";
    private static final String S_HOUR_OF_DAY = "hourOfDay";
    private static final String S_UNIT = "unit";
    private static final String S_PLAYER = "player";
    private static final String S_ALLY = "ally";
    private static final String S_MONSTER = "monster";
    private static final String S_VILLAGER = "villager";
    private static final String S_ITEM = "item";
    private static final String S_ID = "id";
    private static final String S_ID_VALUE = "val";
    private static final String S_COUNT = "count";
    private static final String S_DIRECTION = "dir";
    private static final String S_POS_X = "pos_x";
    private static final String S_POS_Y = "pos_y";
    private static final String S_POS_TILE_X = "posTileX";
    private static final String S_POS_TILE_Y = "posTileY";
    private static final String S_NAME = "name";
    private static final String S_HP = "hp";
    private static final String S_COOLDOWN = "cool";
    private static final String S_ITEM_TYPE = "itemType";
    private static final String S_VALUE = "value";
    private static final String S_VILLAGER_TYPE = "vilagerType";
    private static final String S_ALLY_TYPE = "allyType";
    private static final String S_MONSTER_TYPE = "monsterType";
    private static final String S_INVENTORY = "inventory";
    private static final String S_ALIVE = "alive";
    private static final String S_OBJECT = "object";
    private static final String S_OBJ_TYPE = "obj_type";
    private static final String S_ANGLE = "angle";
    private static final String S_DISPLAYED = "displayed";
    private static final String S_HELP = "helping";
    private static final String S_USABLE = "usable";
    private static final String S_IN_USE = "inUse";
    private static final String S_TIMER = "timer";
    private static final String S_EXPLODABLE = "explodable";
    private static final String S_DESTROYED = "destroyed";
    private static final String S_ON_MAP = "onMap";
    private static final String S_TEXT = "text";
    private static final String S_DISCOVERED = "discovered";
    private static final String S_SOUNDS = "sounds";
    private static final String S_CHILD_TYPE = "childType";
    private static final String S_MENU = "menu";
    private static final String S_RIGHT = "right";
    private static final String S_CENTER = "center";
    private static final String MENU_OBJECT = "object",
            MENU_TITLE = "title",
            MENU_TAB = "tab",
            MENU_TYPE = "type",
            MENU_COLOR = "color",
            MENU_MIN = "min",
            MENU_MAX = "max",
            MENU_VALUE = "value",
            MENU_OK_CANCEL = "okCancel",
            MENU_CONTENTS = "contents",
            MENU_LINK = "link",
            MENU_HELP = "HELP",
            MENU_SETTINGS = "SETTINGS",
            MENU_ABOUT = "ABOUT";
    private static final String W_TELEPORT_X = "TELEPORT_X",
            W_TELEPORT_Y = "TELEPORT_Y";
    private static final String W_MAX_DROPS = "MAX_DROPS",
            W_TIMER_MAX = "TIMER_MAX",
            W_RAIN_LENGTH = "RAIN_LENGTH",
            W_RAIN_GAP = "RAIN_GAP",
            W_ONE_HOUR = "ONE_HOUR",
            W_HOURS_PER_DAY = "HOURS_PER_DAY",
            W_STRIKE_DAMAGE = "STRIKE_DAMAGE",
            W_STRIKE_RANGE = "STRIKE_RANGE",
            W_LIGHTNING_INTERVAL = "LIGHTNING_INTERVAL";
    private static final String I_AMULET_HP = "AMULET_HP",
            I_SWORD_DAMAGE = "SWORD_DAMAGE",
            I_TOME_COOLDOWN = "TOME_COOLDOWN",
            I_BOW_RANGE_DAMAGE = "BOW_RANGE_DAMAGE",
            I_EXPLODING_DAMAGE = "EXPLODING_DAMAGE",
            I_EXPLODING_TIME = "EXPLODING_TIME",
            I_EXPLODING_RANGE = "EXPLODING_RANGE",
            I_POTION_HEAL = "POTION_HEAL",
            I_ARMOR_DEFENCE = "ARMOR_DEFENCE",
            I_RING_HEALRATE = "RING_HEALRATE",
            I_CLOAK_SPEED = "CLOAK_SPEED";
    private static final String U_DEFAULT_SPEED = "DEFAULT_SPEED",
            U_DEFAULT_MAX_HP = "DEFAULT_MAX_HP",
            U_DEFAULT_AUTO_HEAL = "DEFAULT_AUTO_HEAL",
            U_IDLE_TIME = "IDLE_TIME",
            U_DIALOG_INTERVAL = "DIALOG_INTERVAL",
            U_SHOW_DEAD = "SHOW_DEAD",
            U_CLOSE_PROXIMITY = "CLOSE_PROXIMITY",
            U_VISIBLITY = "VISIBLITY",
            U_PROJECTILE_RANGE = "PROJECTILE_RANGE";
    private static final String P_DAMAGE = "PLAYER_DAMAGE",
            P_COOLDOWN = "PLAYER_COOLDOWN",
            P_DEFENCE = "PLAYER_DEFENCE",
            P_STATUS_MAX = "STATUS_MAX",
            P_STATUS_DURATION = "STATUS_DURATION",
            P_SHOW_STATUS = "SHOW_STATUS";
    private static final String A_ENEMY_RANGE = "ENEMY_RANGE",
            A_ALLY_RANGE = "ALLY_RANGE",
            A_ALLY_DISTANCE = "ALLY_DISTANCE",
            A_MOVE_BACK = "MOVE_BACK",
            A_COIN_AMOUNT = "COIN_AMOUNT",
            A_BOWMAN_MAX_HP = "BOWMAN_MAX_HP",
            A_BOWMAN_DAMAGE = "BOWMAN_DAMAGE",
            A_BOWMAN_RANGE_DAMAGE = "BOWMAN_RANGE_DAMAGE",
            A_BOWMAN_COOLDOWN = "BOWMAN_COOLDOWN",
            A_SWORDMAN_MAX_HP = "SWORDMAN_MAX_HP",
            A_SWORDMAN_DAMAGE = "SWORDMAN_DAMAGE",
            A_SWORDMAN_RANGE_DAMAGE = "SWORDMAN_RANGE_DAMAGE",
            A_SWORDMAN_COOLDOWN = "SWORDMAN_COOLDOWN",
            A_SPEARMAN_MAX_HP = "SPEARMAN_MAX_HP",
            A_SPEARMAN_DAMAGE = "SPEARMAN_DAMAGE",
            A_SPEARMAN_RANGE_DAMAGE = "SPEARMAN_RANGE_DAMAGE",
            A_SPEARMAN_COOLDOWN = "SPEARMAN_COOLDOWN",
            A_GENERAL_MAX_HP = "GENERAL_MAX_HP",
            A_GENERAL_DAMAGE = "GENERAL_DAMAGE",
            A_GENERAL_RANGE_DAMAGE = "GENERAL_RANGE_DAMAGE",
            A_GENERAL_COOLDOWN = "GENERAL_COOLDOWN";
    private static final String V_HEAL_RATE = "HEAL_RATE";
    private static final String M_ZOMBIE_MAX_HP = "ZOMBIE_MAX_HP",
            M_ZOMBIE_DAMAGE = "ZOMBIE_DAMAGE",
            M_ZOMBIE_RANGE_DAMAGE = "ZOMBIE_RANGE_DAMAGE",
            M_ZOMBIE_COOLDOWN = "ZOMBIE_COOLDOWN",
            M_BANDIT_MAX_HP = "BANDIT_MAX_HP",
            M_BANDIT_DAMAGE = "BANDIT_DAMAGE",
            M_BANDIT_RANGE_DAMAGE = "BANDIT_RANGE_DAMAGE",
            M_BANDIT_COOLDOWN = "BANDIT_COOLDOWN",
            M_SKELETON_MAX_HP = "SKELETON_MAX_HP",
            M_SKELETON_DAMAGE = "SKELETON_DAMAGE",
            M_SKELETON_RANGE_DAMAGE = "SKELETON_RANGE_DAMAGE",
            M_SKELETON_COOLDOWN = "SKELETON_COOLDOWN",
            M_DRAELIC_MAX_HP = "DRAELIC_MAX_HP",
            M_DRAELIC_DAMAGE = "DRAELIC_DAMAGE",
            M_DRAELIC_RANGE_DAMAGE = "DRAELIC_RANGE_DAMAGE",
            M_DRAELIC_COOLDOWN = "DRAELIC_COOLDOWN",
            M_DRAGON_MAX_HP = "DRAGON_MAX_HP",
            M_DRAGON_DAMAGE = "DRAGON_DAMAGE",
            M_DRAGON_RANGE_DAMAGE = "DRAGON_RANGE_DAMAGE",
            M_DRAGON_COOLDOWN = "DRAGON_COOLDOWN";
    private static final String O_DEFAULT_SPEED = "DEFAULT_SPEED",
            O_IDLE_TIME = "IDLE_TIME";
    private static final String SM_MUSIC = "music",
            SM_MUSIC_VOL = "musicVolume",
            SM_FX = "fx",
            SM_FX_VOL = "fxVolume";
    private static final String ST_PLAYER_NAME = "playerName";

    /** Load dialogues from a specified XML file.
     * @param world The game world.
     * @param file The path of the file name to which the data should be saved.
     */
    public static boolean loadDialogues(String fileName)
            throws XMLStreamException, IOException, ParserConfigurationException, SAXException {

        FontLoader.getDialogues().clear();

        File file = new File(fileName);

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(file);
        doc.getDocumentElement().normalize();

        if (!doc.getDocumentElement().getNodeName().equals(S_GAME)) {
            return false;
        }

        // load the dialogue data
        NodeList nodeList = doc.getElementsByTagName(S_ID);
        if (nodeList.getLength() > 0) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node child = nodeList.item(i);
                while (child != null) {
                    if (child.getNodeName().equals(S_TEXT)) {
                        FontLoader.getDialogues().add(child.getFirstChild().getNodeValue());
                    }
                    child = child.getNextSibling();
                }
            }
        }

        return true;

    }

    /** Load a game from a specified XML file.
     * @param world The game world.
     * @param file The path of the file name to which the data should be saved.
     */
    public static boolean loadGame(World world, String fileName)
            throws XMLStreamException, IOException, ParserConfigurationException, SAXException {

        world.clearObjects();

        File file = new File(fileName);

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(file);
        doc.getDocumentElement().normalize();

        if (!doc.getDocumentElement().getNodeName().equals(S_WORLD)) {
            return false;
        }

        // load the weather
        NodeList nodeList = doc.getElementsByTagName(S_WEATHER);
        if (nodeList.getLength() < 1) {
            return false;
        }
        if (!readWeatherFromFile(world.getWeatherGen(), nodeList.item(0).getChildNodes())) {
            return false;
        }


        // load the items
        nodeList = doc.getElementsByTagName(S_ITEM);
        if (nodeList.getLength() > 0) {
            nodeList = nodeList.item(0).getChildNodes();
        } else {
            return false;
        }
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node child = nodeList.item(i);
            if ((child.getNodeType() == Node.ELEMENT_NODE) && (child.getNodeName().equals(S_ID))) {
                if (!readItemFromFile(world.getItems(), child.getChildNodes(), world)) {
                    return false;
                }
            }
        }

        // load the player
        nodeList = doc.getElementsByTagName(S_PLAYER);
        if (nodeList.getLength() > 0) {
            nodeList = nodeList.item(0).getChildNodes();
        } else {
            return false;
        }
//		if (!readUnitFromFile(world, nodeList, Unit.UnitType.PLAYER)) { return false; }

        // load the villagers
        nodeList = doc.getElementsByTagName(S_VILLAGER);
        if (nodeList.getLength() > 0) {
            nodeList = nodeList.item(0).getChildNodes();
        } else {
            return false;
        }
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node child = nodeList.item(i);
            if ((child.getNodeType() == Node.ELEMENT_NODE) && (child.getNodeName().equals(S_ID))) {
//				if (!readUnitFromFile(world, child.getChildNodes(), Unit.UnitType.VILLAGER)) { return false; }
            }
        }

        // load the allies
        nodeList = doc.getElementsByTagName(S_ALLY);
        if (nodeList.getLength() > 0) {
            nodeList = nodeList.item(0).getChildNodes();
        } else {
            return false;
        }
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node child = nodeList.item(i);
            if ((child.getNodeType() == Node.ELEMENT_NODE) && (child.getNodeName().equals(S_ID))) {
//				if (!readUnitFromFile(world, child.getChildNodes(), Unit.UnitType.ALLY)) { return false; }
            } else if ((child.getNodeType() == Node.ATTRIBUTE_NODE) && (child.getNodeName().equals(S_HELP))) {
//				Ally.setHelping(Boolean.parseBoolean(child.getNodeValue())); 
            } else if ((child.getNodeType() == Node.ATTRIBUTE_NODE) && (child.getNodeName().equals(S_DISCOVERED))) {
//				Ally.setDiscovered(Boolean.parseBoolean(child.getNodeValue())); 
            }
        }

        // load the monsters
        nodeList = doc.getElementsByTagName(S_MONSTER);
        if (nodeList.getLength() > 0) {
            nodeList = nodeList.item(0).getChildNodes();
        } else {
            return false;
        }
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node child = nodeList.item(i);
            if ((child.getNodeType() == Node.ELEMENT_NODE) && (child.getNodeName().equals(S_ID))) {
//				if (!readUnitFromFile(world, child.getChildNodes(), Unit.UnitType.MONSTER)) { return false; }
            }
        }

        // load the other objects
        nodeList = doc.getElementsByTagName(S_OBJECT);
        if (nodeList.getLength() > 0) {
            nodeList = nodeList.item(0).getChildNodes();
        } else {
            return false;
        }
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node child = nodeList.item(i);
            if ((child.getNodeType() == Node.ELEMENT_NODE) && (child.getNodeName().equals(S_ID))) {
                if (!readObjectFromFile(world.getObjects(), child.getChildNodes(), world)) {
                    return false;
                }
            }
        }

        return true;

    }

    private static boolean readWeatherFromFile(Weather weather, NodeList list) {

        String hour = "";

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(S_HOUR_OF_DAY)) {
                    hour = child.getFirstChild().getNodeValue();
                }
            }
        }

        try {
            weather.setHourOfDay(Float.parseFloat(hour));
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return false;
        }

        return true;

    }

    private static boolean readItemFromFile(ArrayList<Item> items, NodeList list, World world) {

        String pos_x = "", pos_y = "", pos_t_x = "", pos_t_y = "",
                value = "", itemType = "", onMap = "", usable = "",
                inUse = "", timer = "";

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(S_POS_X)) {
                    pos_x = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_Y)) {
                    pos_y = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_TILE_X)) {
                    pos_t_x = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_TILE_Y)) {
                    pos_t_y = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_VALUE)) {
                    value = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_ON_MAP)) {
                    onMap = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_USABLE)) {
                    usable = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_IN_USE)) {
                    inUse = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_TIMER)) {
                    timer = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_ITEM_TYPE)) {
                    itemType = child.getFirstChild().getNodeValue();
                }
            }
        }

        try {

            Item item = new Item(null, itemType, itemType, Integer.parseInt(value.equals("") ? "1" : value));

            if (pos_x.equals("") || pos_y.equals("")) {
                float halfTile = 0.5f;
                float x = (Integer.parseInt(pos_t_x) + halfTile) * world.getMap().getTileWidth();
                float y = (Integer.parseInt(pos_t_y) + halfTile) * world.getMap().getTileHeight();
                item.moveTo(x, y);
            } else {
                item.moveTo(Float.parseFloat(pos_x), Float.parseFloat(pos_y));
            }

            if (usable.length() > 0) {
                item.setUsable(Boolean.parseBoolean(usable));
            }
            if (inUse.length() > 0) {
                item.setUsable(Boolean.parseBoolean(inUse));
            }
            if (timer.length() > 0) {
                item.setTimer(Integer.parseInt(timer));
            }
            if (onMap.length() > 0) {
                item.setOnMap(Boolean.parseBoolean(onMap));
            }
            items.add(item);

        } catch (NumberFormatException e) {
            e.printStackTrace();
            return false;
        } catch (SlickException e) {
            e.printStackTrace();
            return false;
        }

        return true;

    }

    private static boolean readNewItemFromFile(ArrayList<Item> items, NodeList list) {

        String value = "", itemType = "";

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(S_VALUE)) {
                    value = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_ITEM_TYPE)) {
                    itemType = child.getFirstChild().getNodeValue();
                }
            }
        }

        try {
            Item item = new Item(null, itemType, itemType, Integer.parseInt(value.equals("") ? "1" : value));
            items.add(item);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return false;
        } catch (SlickException e) {
            e.printStackTrace();
            return false;
        }

        return true;

    }

    private static ArrayList<Integer> readItemIDFromFile(NodeList list, World world) {

        ArrayList<Integer> items = new ArrayList<Integer>();
        if (list.getLength() < 1) {
            return items;
        }
        Node child = list.item(0);

        while (child != null) {
            String nodeName = child.getNodeName();
            if (nodeName.equals(S_ID)) {
                items.add(Integer.parseInt(child.getFirstChild().getNodeValue()));
            } else if (nodeName.equals(S_ITEM)) {
                int i = world.getItems().size();
                if (readNewItemFromFile(world.getItems(), child.getChildNodes())) {
                    items.add(i);
                }
            }
            child = child.getNextSibling();
        }

        return items;

    }

//	private static boolean readUnitFromFile(World world, NodeList list, Unit.UnitType type) {
    private static boolean readUnitFromFile(World world, NodeList list, Unit type) {

        ArrayList<Integer> inventory = null;

        String name = "", pos_x = "", pos_y = "", pos_t_x = "", pos_t_y = "", alive = "",
                hp = "", cool = "", subType = "", childType = "", dir = "";

        String subTypeNode = String.valueOf(type);
//		if (type == Unit.UnitType.VILLAGER) {
//			subTypeNode = S_VILLAGER_TYPE;
//		} else if (type == Unit.UnitType.ALLY) {
//			subTypeNode = S_ALLY_TYPE;
//		} else if (type == Unit.UnitType.MONSTER) {
//			subTypeNode = S_MONSTER_TYPE;
//		}

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(S_NAME)) {
                    name = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_X)) {
                    pos_x = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_Y)) {
                    pos_y = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_TILE_X)) {
                    pos_t_x = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_TILE_Y)) {
                    pos_t_y = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_ALIVE)) {
                    alive = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_DIRECTION)) {
                    dir = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_HP)) {
                    hp = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_COOLDOWN)) {
                    cool = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(subTypeNode)) {
                    subType = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_CHILD_TYPE)) {
                    childType = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_INVENTORY)) {
                    inventory = readItemIDFromFile(child.getChildNodes(), world);
                }
            }
        }

        if (inventory == null) {
            return false;
        }

        try {
//			if (type == Unit.UnitType.PLAYER) {
//				Player player = new Player(world,Player.DEFAULT_FRAME_WIDTH,Player.DEFAULT_FRAME_HEIGHT);
//				if (name.length()>0) { player.setName(name); }
//				loadUnitDetails(player, world, inventory, pos_x, pos_y, pos_t_x, pos_t_y, alive, hp, cool, dir);
//				world.setPlayer(player);
//			} else if (type == Unit.UnitType.VILLAGER) {
//				Villager v = new Villager(Villager.VillagerType.valueOf(subType),Villager.DEFAULT_FRAME_WIDTH,Villager.DEFAULT_FRAME_HEIGHT);
//				loadUnitDetails(v, world, inventory, pos_x, pos_y, pos_t_x, pos_t_y, alive, hp, cool, dir);
//				world.getVillagers().add(v);
//				world.getAllOtherUnits().add(v);
//			} else if (type == Unit.UnitType.ALLY) {
//				Ally a = new Ally(Ally.AllyType.valueOf(subType),Ally.DEFAULT_FRAME_WIDTH,Ally.DEFAULT_FRAME_HEIGHT);
//				loadUnitDetails(a, world, inventory, pos_x, pos_y, pos_t_x, pos_t_y, alive, hp, cool, dir);
//				world.getAllies().add(a);
//				world.getAllOtherUnits().add(a);
//				if (a.isFlyingUnit()) { world.getFlyingUnitsOnly().add(a); }
//			} else if (type == Unit.UnitType.MONSTER) {
//				Monster.MonsterType mType = Monster.MonsterType.valueOf(subType);
//				Monster m;
//				if (mType == Monster.MonsterType.LEADER) {
//					if (childType.length() == 0) { return false; }
//					m = new LeaderMonster(LeaderMonster.LeaderMonsterType.valueOf(childType),LeaderMonster.DEFAULT_FRAME_WIDTH,LeaderMonster.DEFAULT_FRAME_HEIGHT);
//				} else if (mType == Monster.MonsterType.FLYING) {
//					if (childType.length() == 0) { return false; }
//					m = new FlyingMonster(FlyingMonster.FlyingMonsterType.valueOf(childType), world, FlyingMonster.DEFAULT_FRAME_WIDTH,FlyingMonster.DEFAULT_FRAME_HEIGHT);
//				} else {
//					m = new Monster(mType,Monster.DEFAULT_FRAME_WIDTH,Monster.DEFAULT_FRAME_HEIGHT);
//				}
//				loadUnitDetails(m, world, inventory, pos_x, pos_y, pos_t_x, pos_t_y, alive, hp, cool, dir);
//				world.getMonsters().add(m);
//				world.getAllOtherUnits().add(m);
//				if (m.isFlyingUnit()) { world.getFlyingUnitsOnly().add(m); }
//			}				
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return false;
//		} catch (SlickException e) {
//			e.printStackTrace();
//			return false;
        }

        return true;

    }

    private static void loadUnitDetails(Unit unit, World world, ArrayList<Integer> inventory,
            String pos_x, String pos_y, String pos_t_x, String pos_t_y,
            String alive, String hp, String cool, String dir) {

        if (pos_x.equals("") || pos_y.equals("")) {
            float halfTile = 0.5f;
            float x = (Integer.parseInt(pos_t_x) + halfTile) * world.getMap().getTileWidth();
            float y = (Integer.parseInt(pos_t_y) + halfTile) * world.getMap().getTileHeight();
            unit.moveTo(x, y);
        } else {
            unit.moveTo(Float.parseFloat(pos_x), Float.parseFloat(pos_y));
        }

        unit.setAlive(Boolean.parseBoolean(alive));

        for (int id : inventory) {
            Item item = Item.searchItem(world.getItems(), id);
            if (item != null) {
                unit.pickUpItem(item);
            }
        }
        if (hp.length() > 0) {
            unit.setHP(Float.parseFloat(hp));
        }
        if (cool.length() > 0) {
            unit.setCoolDown(Integer.parseInt(cool));
        }
        if (dir.length() == 0) {
            unit.standInRandomDirection();
        } else {
            unit.setDirection(Unit.Direction.valueOf(dir));
            unit.standInDefaultDirection();
        }

    }

    private static boolean readObjectFromFile(ArrayList<OtherObject> objs, NodeList list, World world) {

        String angle = "", displayed = "true",
                pos_x = "", pos_y = "",
                pos_t_x = "", pos_t_y = "",
                objType = "", explodable = "",
                destroyed = "", onMap = "";

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(S_ANGLE)) {
                    angle = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_DISPLAYED)) {
                    displayed = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_X)) {
                    pos_x = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_Y)) {
                    pos_y = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_TILE_X)) {
                    pos_t_x = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_TILE_Y)) {
                    pos_t_y = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_EXPLODABLE)) {
                    explodable = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_DESTROYED)) {
                    destroyed = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_ON_MAP)) {
                    onMap = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_OBJ_TYPE)) {
                    objType = child.getFirstChild().getNodeValue();
                }
            }

        }

        if (angle.length() == 0) {
            angle = "0";
        }

        try {

            OtherObject o = new OtherObject(OtherObject.ObjectType.valueOf(objType), Float.parseFloat(angle));

            if (pos_x.equals("") || pos_y.equals("")) {
                float halfTile = 0.5f;
                float x = (Integer.parseInt(pos_t_x) + halfTile) * world.getMap().getTileWidth();
                float y = (Integer.parseInt(pos_t_y) + halfTile) * world.getMap().getTileHeight();
                o.moveTo(x, y);
            } else {
                o.moveTo(Float.parseFloat(pos_x), Float.parseFloat(pos_y));
            }

            if (displayed.length() > 0) {
                o.setDisplayed(Boolean.parseBoolean(displayed));
            }
            if (explodable.length() > 0) {
                o.setExplodable(Boolean.parseBoolean(explodable));
            }
            if (destroyed.length() > 0) {
                o.setDestroyed(Boolean.parseBoolean(destroyed));
            }
            if (onMap.length() > 0) {
                o.setOnMap(Boolean.parseBoolean(onMap));
            }
            objs.add(o);

        } catch (NumberFormatException e) {
            e.printStackTrace();
            return false;
        } catch (SlickException e) {
            e.printStackTrace();
            return false;
        }

        return true;

    }

    /** Save the game into a specified XML file.
     * @param world The world from which the details are saved.
     * @param file The path of the file name to which the data should be saved.
     * @throws XMLStreamException
     * @throws IOException
     */
    public static void saveGame(World world, String file)
            throws XMLStreamException, IOException {

        XMLOutputFactory factory = XMLOutputFactory.newInstance();
        XMLStreamWriter writer = factory.createXMLStreamWriter(new FileWriter(file));

        writer.writeStartDocument("1.0");
        writer.writeStartElement(S_WORLD);

        // write the weather details
        writer.writeStartElement(S_WEATHER);
        writeWeatherToFile(writer, world.getWeatherGen());
        writer.writeEndElement();

        // write the item details
        writer.writeStartElement(S_ITEM);
        writer.writeAttribute(S_COUNT, String.valueOf(world.getItems().size()));
        for (Item i : world.getItems()) {
            writer.writeStartElement(S_ID);
            writer.writeAttribute(S_ID_VALUE, String.valueOf(i.getId()));
            writeItemToFile(writer, i);
            writer.writeEndElement();
        }
        writer.writeEndElement();

        // write the player details
        writer.writeStartElement(S_PLAYER);
        writePlayerToFile(writer, world.getPlayer());
        writer.writeEndElement();

        // write the villager details
        writer.writeStartElement(S_VILLAGER);
//	    writer.writeAttribute(S_COUNT, String.valueOf(world.getVillagers().size()));
//	    for (int i = 0 ; i < world.getVillagers().size() ; i++) {
//	    	writer.writeStartElement(S_ID);
//	 	    writer.writeAttribute(S_ID_VALUE, String.valueOf(i));
//	    	writeVillagerToFile(writer, world.getVillagers().elementAt(i));
//	    	writer.writeEndElement();
//	    }
        writer.writeEndElement();

        // write the ally details
        writer.writeStartElement(S_ALLY);
//	    writer.writeAttribute(S_DISCOVERED, String.valueOf(Ally.isDiscovered()));
//	    writer.writeAttribute(S_HELP, String.valueOf(Ally.isHelping()));
//	    writer.writeAttribute(S_COUNT, String.valueOf(world.getAllies().size()));
//	    for (int i = 0 ; i < world.getAllies().size() ; i++) {
//		    writer.writeStartElement(S_ID);
//	 	    writer.writeAttribute(S_ID_VALUE, String.valueOf(i));
//	    	writeAllyToFile(writer, world.getAllies().elementAt(i));
//	    	writer.writeEndElement();
//	    }
        writer.writeEndElement();

        // write the monster details
        writer.writeStartElement(S_MONSTER);
//	    writer.writeAttribute(S_COUNT, String.valueOf(world.getMonsters().size()));
//	    for (int i = 0 ; i < world.getMonsters().size() ; i++) {
//		    writer.writeStartElement(S_ID);
//	 	    writer.writeAttribute(S_ID_VALUE, String.valueOf(i));
////	    	writeMonsterToFile(writer, world.getMonsters().elementAt(i));
//	    	writer.writeEndElement();
//		}
        writer.writeEndElement();

        // write the other object details
        writer.writeStartElement(S_OBJECT);
        writer.writeAttribute(S_COUNT, String.valueOf(world.getObjects().size()));
        for (OtherObject o : world.getObjects()) {
            writer.writeStartElement(S_ID);
            writer.writeAttribute(S_ID_VALUE, String.valueOf(o.getId()));
            writeObjectToFile(writer, o);
            writer.writeEndElement();
        }
        writer.writeEndElement();

        writer.writeEndDocument();
        writer.flush();
        writer.close();

    }

    private static void writeItemToFile(XMLStreamWriter writer, Item item)
            throws XMLStreamException {

        writer.writeStartElement(S_POS_X);
        writer.writeCharacters(String.valueOf(item.getX()));
        writer.writeEndElement();

        writer.writeStartElement(S_POS_Y);
        writer.writeCharacters(String.valueOf(item.getY()));
        writer.writeEndElement();

        writer.writeStartElement(S_VALUE);
        writer.writeCharacters(String.valueOf(item.getValue()));
        writer.writeEndElement();

        writer.writeStartElement(S_ON_MAP);
        writer.writeCharacters(String.valueOf(item.isOnMap()));
        writer.writeEndElement();

        writer.writeStartElement(S_USABLE);
        writer.writeCharacters(String.valueOf(item.isUsable()));
        writer.writeEndElement();

        writer.writeStartElement(S_IN_USE);
        writer.writeCharacters(String.valueOf(item.isInUse()));
        writer.writeEndElement();

        writer.writeStartElement(S_TIMER);
        writer.writeCharacters(String.valueOf(item.getTimer()));
        writer.writeEndElement();

        writer.writeStartElement(S_ITEM_TYPE);
        writer.writeCharacters(String.valueOf(item.getItemType()));
        writer.writeEndElement();

    }

    private static void writeCommonUnitDetailsToFile(XMLStreamWriter writer, Unit unit)
            throws XMLStreamException {

        writer.writeStartElement(S_POS_X);
        writer.writeCharacters(String.valueOf(unit.getX()));
        writer.writeEndElement();

        writer.writeStartElement(S_POS_Y);
        writer.writeCharacters(String.valueOf(unit.getY()));
        writer.writeEndElement();

        writer.writeStartElement(S_ALIVE);
        writer.writeCharacters(String.valueOf(unit.isAlive()));
        writer.writeEndElement();

        writer.writeStartElement(S_DIRECTION);
        writer.writeCharacters(String.valueOf(unit.getDirection()));
        writer.writeEndElement();

        writer.writeStartElement(S_HP);
        writer.writeCharacters(String.valueOf(unit.getHP()));
        writer.writeEndElement();

        writer.writeStartElement(S_COOLDOWN);
        writer.writeCharacters(String.valueOf(unit.getCoolDown()));
        writer.writeEndElement();

        writer.writeStartElement(S_INVENTORY);
        writer.writeAttribute(S_COUNT, String.valueOf(unit.getInventory().size()));
        for (Item i : unit.getInventory()) {
            writer.writeStartElement(S_ID);
            writer.writeCharacters(String.valueOf(i.getId()));
            writer.writeEndElement();
            ;
        }
        writer.writeEndElement();

    }

    private static void writePlayerToFile(XMLStreamWriter writer, Player player)
            throws XMLStreamException {

        writeCommonUnitDetailsToFile(writer, player);

    }

    private static void writeVillagerToFile(XMLStreamWriter writer, Villager villager)
            throws XMLStreamException {

        writeCommonUnitDetailsToFile(writer, villager);

        writer.writeStartElement(S_VILLAGER_TYPE);
//	    writer.writeCharacters(String.valueOf(villager.getVillagerType()));
        writer.writeEndElement();


    }

    private static void writeAllyToFile(XMLStreamWriter writer, Ally ally)
            throws XMLStreamException {

        writeCommonUnitDetailsToFile(writer, ally);

        writer.writeStartElement(S_ALLY_TYPE);
//	    writer.writeCharacters(String.valueOf(ally.getAllyType()));
        writer.writeEndElement();


    }

//	private static void writeMonsterToFile(XMLStreamWriter writer, Monster monster)
//	throws XMLStreamException {
//		
//		writeCommonUnitDetailsToFile(writer, monster);
//		
//		writer.writeStartElement(S_MONSTER_TYPE);
//	    writer.writeCharacters(String.valueOf(monster.getMonsterType()));
//	    writer.writeEndElement();
//	    
//	    if (monster.getMonsterType() == Monster.MonsterType.FLYING) {
//	    	FlyingMonster fm = (FlyingMonster) monster;
//	    	writer.writeStartElement(S_CHILD_TYPE);
//	    	writer.writeCharacters(String.valueOf(fm.getFlyingMonsterType()));
//	    	writer.writeEndElement();
//	    } else if (monster.getMonsterType() == Monster.MonsterType.LEADER) {
//	    	LeaderMonster lm = (LeaderMonster) monster;
//	    	writer.writeStartElement(S_CHILD_TYPE);
//	    	writer.writeCharacters(String.valueOf(lm.getLeaderMonsterType()));
//	    	writer.writeEndElement();
//	    }
//		
//	}
    private static void writeObjectToFile(XMLStreamWriter writer, OtherObject obj)
            throws XMLStreamException {

        writer.writeStartElement(S_POS_X);
        writer.writeCharacters(String.valueOf(obj.getX()));
        writer.writeEndElement();

        writer.writeStartElement(S_POS_Y);
        writer.writeCharacters(String.valueOf(obj.getY()));
        writer.writeEndElement();

        writer.writeStartElement(S_ANGLE);
        writer.writeCharacters(String.valueOf(obj.getAngle()));
        writer.writeEndElement();

        writer.writeStartElement(S_ON_MAP);
        writer.writeCharacters(String.valueOf(obj.isOnMap()));
        writer.writeEndElement();

        writer.writeStartElement(S_EXPLODABLE);
        writer.writeCharacters(String.valueOf(obj.isExplodable()));
        writer.writeEndElement();

        writer.writeStartElement(S_DESTROYED);
        writer.writeCharacters(String.valueOf(obj.isDestroyed()));
        writer.writeEndElement();

        writer.writeStartElement(S_OBJ_TYPE);
        writer.writeCharacters(String.valueOf(obj.getObjectType()));
        writer.writeEndElement();

    }

    private static void writeWeatherToFile(XMLStreamWriter writer, Weather weather)
            throws XMLStreamException {

        writer.writeStartElement(S_HOUR_OF_DAY);
        writer.writeCharacters(String.valueOf(weather.getHourOfDay()));
        writer.writeEndElement();

    }

    /** Load a game data from a specified XML file.
     * @param world The game world.
     * @param file The path of the file name to which the data should be saved.
     */
    public static void loadGameData(String fileName)
            throws XMLStreamException, IOException, ParserConfigurationException, SAXException {

        File file = new File(fileName);

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(file);
        doc.getDocumentElement().normalize();

        if (!doc.getDocumentElement().getNodeName().equals(S_GAME)) {
            return;
        }

        // load the world data
        NodeList nodeList = doc.getElementsByTagName(S_WORLD);
        if (nodeList.getLength() > 0) {
            initialiseWorld(nodeList.item(0).getChildNodes());
        }

        // load the weather data
        nodeList = doc.getElementsByTagName(S_WEATHER);
        if (nodeList.getLength() > 0) {
            initialiseWeather(nodeList.item(0).getChildNodes());
        }

        // load the item data
        nodeList = doc.getElementsByTagName(S_ITEM);
        if (nodeList.getLength() > 0) {
            initialiseItem(nodeList.item(0).getChildNodes());
        }

        // load the unit data
        nodeList = doc.getElementsByTagName(S_UNIT);
        if (nodeList.getLength() > 0) {
            initialiseUnit(nodeList.item(0).getChildNodes());
        }

        // load the player data
        nodeList = doc.getElementsByTagName(S_PLAYER);
        if (nodeList.getLength() > 0) {
            initialisePlayer(nodeList.item(0).getChildNodes());
        }

        // load the ally data
        nodeList = doc.getElementsByTagName(S_ALLY);
        if (nodeList.getLength() > 0) {
            initialiseAlly(nodeList.item(0).getChildNodes());
        }

        // load the villager data
        nodeList = doc.getElementsByTagName(S_VILLAGER);
        if (nodeList.getLength() > 0) {
            initialiseVillager(nodeList.item(0).getChildNodes());
        }

        // load the monster data
        nodeList = doc.getElementsByTagName(S_MONSTER);
        if (nodeList.getLength() > 0) {
            initialiseMonster(nodeList.item(0).getChildNodes());
        }

        // load the other objects data
        nodeList = doc.getElementsByTagName(S_OBJECT);
        if (nodeList.getLength() > 0) {
            initialiseObject(nodeList.item(0).getChildNodes());
        }

    }

    private static void initialiseWorld(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(W_TELEPORT_X)) {
                    World.TELEPORT_X = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_TELEPORT_Y)) {
                    World.TELEPORT_Y = Float.parseFloat(child.getFirstChild().getNodeValue());
                }
            }
        }

    }

    private static void initialiseWeather(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(W_MAX_DROPS)) {
                    Weather.MAX_DROPS = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_TIMER_MAX)) {
                    Weather.TIMER_MAX = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_RAIN_LENGTH)) {
                    Weather.RAIN_LENGTH = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_RAIN_GAP)) {
                    Weather.RAIN_GAP = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_ONE_HOUR)) {
                    Weather.ONE_HOUR = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_HOURS_PER_DAY)) {
                    Weather.HOURS_PER_DAY = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_LIGHTNING_INTERVAL)) {
                    Weather.LIGHTNING_INTERVAL = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_STRIKE_DAMAGE)) {
                    Lightning.STRIKE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(W_STRIKE_RANGE)) {
                    Lightning.STRIKE_RANGE = Integer.parseInt(child.getFirstChild().getNodeValue());
                }
            }
        }

    }

    private static void initialiseItem(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(I_AMULET_HP)) {
                    Item.AMULET_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_SWORD_DAMAGE)) {
                    Item.SWORD_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_TOME_COOLDOWN)) {
                    Item.TOME_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_BOW_RANGE_DAMAGE)) {
                    Item.BOW_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_EXPLODING_DAMAGE)) {
                    //Item.EXPLODING_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_EXPLODING_TIME)) {
                    //Item.EXPLODING_TIME = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_EXPLODING_RANGE)) {
                    //Item.EXPLODING_RANGE = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_ARMOR_DEFENCE)) {
                    Item.ARMOR_DEFENCE = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_POTION_HEAL)) {
                    Item.POTION_HEAL = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_RING_HEALRATE)) {
                    Item.RING_HEALRATE = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(I_CLOAK_SPEED)) {
                    Item.CLOAK_SPEED = Float.parseFloat(child.getFirstChild().getNodeValue());
                }
            }
        }

    }

    private static void initialiseUnit(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(U_DEFAULT_SPEED)) {
                    Unit.DEFAULT_SPEED = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_DEFAULT_MAX_HP)) {
                    Unit.DEFAULT_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_DEFAULT_AUTO_HEAL)) {
//					Unit.DEFAULT_AUTO_HEAL = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_IDLE_TIME)) {
                    Unit.IDLE_TIME = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_DIALOG_INTERVAL)) {
                    Unit.DIALOG_INTERVAL = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_SHOW_DEAD)) {
                    Unit.SHOW_DEAD = Integer.parseInt(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_CLOSE_PROXIMITY)) {
                    Unit.CLOSE_PROXIMITY = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_VISIBLITY)) {
                    Unit.VISIBLITY = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(U_PROJECTILE_RANGE)) {
                    Unit.PROJECTILE_RANGE = Float.parseFloat(child.getFirstChild().getNodeValue());
                }
            }
        }

    }

    private static void initialisePlayer(NodeList list) {
//		
//		for (int i = 0 ; i < list.getLength() ; i++) {
//			Node child = list.item(i);
//			if (child.getNodeType() == Node.ELEMENT_NODE) {
//				String nodeName = child.getNodeName();
//				if (nodeName == P_DAMAGE) {
//					Player.PLAYER_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == P_COOLDOWN) {
//					Player.PLAYER_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == P_DEFENCE) {
//					Player.PLAYER_DEFENCE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == P_STATUS_MAX) {
//					Player.STATUS_MAX = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == P_STATUS_DURATION) {
//					Player.STATUS_DURATION = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == P_SHOW_STATUS) {
//					Player.SHOW_STATUS = Boolean.parseBoolean(child.getFirstChild().getNodeValue());
//				}
//			}
//		}
    }

    private static void initialiseAlly(NodeList list) {
//		
//		for (int i = 0 ; i < list.getLength() ; i++) {
//			Node child = list.item(i);
//			if (child.getNodeType() == Node.ELEMENT_NODE) {
//				String nodeName = child.getNodeName();
//				if (nodeName == A_ENEMY_RANGE) {
//					Ally.ENEMY_RANGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_ALLY_RANGE) {
//					Ally.ALLY_RANGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_ALLY_DISTANCE) {
//					Ally.ALLY_DISTANCE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_MOVE_BACK) {
//					Ally.MOVE_BACK = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_COIN_AMOUNT) {
//					Ally.COIN_AMOUNT = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_BOWMAN_COOLDOWN) {
//					Ally.BOWMAN_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_BOWMAN_DAMAGE) {
//					Ally.BOWMAN_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_BOWMAN_MAX_HP) {
//					Ally.BOWMAN_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_BOWMAN_RANGE_DAMAGE) {
//					Ally.BOWMAN_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SWORDMAN_COOLDOWN) {
//					Ally.SWORDMAN_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SWORDMAN_DAMAGE) {
//					Ally.SWORDMAN_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SWORDMAN_MAX_HP) {
//					Ally.SWORDMAN_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SWORDMAN_RANGE_DAMAGE) {
//					Ally.SWORDMAN_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SPEARMAN_COOLDOWN) {
//					Ally.SPEARMAN_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SPEARMAN_DAMAGE) {
//					Ally.SPEARMAN_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SPEARMAN_MAX_HP) {
//					Ally.SPEARMAN_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_SPEARMAN_RANGE_DAMAGE) {
//					Ally.SPEARMAN_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_GENERAL_COOLDOWN) {
//					Ally.GENERAL_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_GENERAL_DAMAGE) {
//					Ally.GENERAL_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_GENERAL_MAX_HP) {
//					Ally.GENERAL_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == A_GENERAL_RANGE_DAMAGE) {
//					Ally.GENERAL_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				}
//			}
//		}
//		
//		Ally.REQUEST = "Bring us "+Ally.COIN_AMOUNT+" Gold Coin"+(Ally.COIN_AMOUNT>1?"s":"")+" and we will help you in your quest.";
//		
    }

    private static void initialiseVillager(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(V_HEAL_RATE)) {
//					Villager.HEAL_RATE = float.parseFloat(child.getFirstChild().getNodeValue());
                }
            }
        }

    }

    private static void initialiseMonster(NodeList list) {
//		for (int i = 0 ; i < list.getLength() ; i++) {
//			Node child = list.item(i);
//			if (child.getNodeType() == Node.ELEMENT_NODE) {
//				String nodeName = child.getNodeName();
//				if (nodeName == M_ZOMBIE_COOLDOWN) {
//					Monster.ZOMBIE_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_ZOMBIE_DAMAGE) {
//					Monster.ZOMBIE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_ZOMBIE_MAX_HP) {
//					Monster.ZOMBIE_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_ZOMBIE_RANGE_DAMAGE) {
//					Monster.ZOMBIE_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_BANDIT_COOLDOWN) {
//					Monster.BANDIT_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_BANDIT_DAMAGE) {
//					Monster.BANDIT_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_BANDIT_MAX_HP) {
//					Monster.BANDIT_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_BANDIT_RANGE_DAMAGE) {
//					Monster.BANDIT_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_SKELETON_COOLDOWN) {
//					Monster.SKELETON_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_SKELETON_DAMAGE) {
//					Monster.SKELETON_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_SKELETON_MAX_HP) {
//					Monster.SKELETON_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_SKELETON_RANGE_DAMAGE) {
//					Monster.SKELETON_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAELIC_COOLDOWN) {
//					LeaderMonster.DRAELIC_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAELIC_DAMAGE) {
//					LeaderMonster.DRAELIC_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAELIC_MAX_HP) {
//					LeaderMonster.DRAELIC_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAELIC_RANGE_DAMAGE) {
//					LeaderMonster.DRAELIC_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAGON_COOLDOWN) {
//					FlyingMonster.DRAGON_COOLDOWN = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAGON_DAMAGE) {
//					FlyingMonster.DRAGON_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAGON_MAX_HP) {
//					FlyingMonster.DRAGON_MAX_HP = Integer.parseInt(child.getFirstChild().getNodeValue());
//				} else if (nodeName == M_DRAGON_RANGE_DAMAGE) {
//					FlyingMonster.DRAGON_RANGE_DAMAGE = Integer.parseInt(child.getFirstChild().getNodeValue());
//				}
//			}
//		}
    }

    private static void initialiseObject(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(O_DEFAULT_SPEED)) {
                    OtherObject.DEFAULT_SPEED = Float.parseFloat(child.getFirstChild().getNodeValue());
                } else if (nodeName.equals(O_IDLE_TIME)) {
                    OtherObject.IDLE_TIME = Integer.parseInt(child.getFirstChild().getNodeValue());
                }
            }
        }

    }

    /** Save the game settings into a specified XML file.
     * @param rpg The RPG game.
     * @param file The path of the file name to which the data should be saved.
     * @throws XMLStreamException
     * @throws IOException
     */
    public static void saveSettings(String file)
            throws XMLStreamException, IOException {

        XMLOutputFactory factory = XMLOutputFactory.newInstance();
        XMLStreamWriter writer = factory.createXMLStreamWriter(new FileWriter(file));

        writer.writeStartDocument("1.0");
        writer.writeStartElement(S_GAME);

        // write the sound setting details
        writer.writeStartElement(S_SOUNDS);
        writer.writeAttribute(S_VALUE, String.valueOf(RPG.getSoundSettings().isPlayingSounds()));
        writeSoundSettingsToFile(writer);
        writer.writeEndElement();

        // write the text settings details
        writer.writeStartElement(S_TEXT);
        writeTextSettingsToFile(writer);
        writer.writeEndElement();

        writer.writeEndDocument();
        writer.flush();
        writer.close();

    }

    private static void writeSoundSettingsToFile(XMLStreamWriter writer)
            throws XMLStreamException {

        writer.writeStartElement(SM_MUSIC);
        writer.writeCharacters(String.valueOf(RPG.getSoundSettings().isBackgroundMusic()));
        writer.writeEndElement();

        writer.writeStartElement(SM_MUSIC_VOL);
        writer.writeCharacters(String.valueOf(RPG.getSoundSettings().getBackgroundMusicVolume()));
        writer.writeEndElement();

        writer.writeStartElement(SM_FX);
        writer.writeCharacters(String.valueOf(RPG.getSoundSettings().isSoundFX()));
        writer.writeEndElement();

        writer.writeStartElement(SM_FX_VOL);
        writer.writeCharacters(String.valueOf(RPG.getSoundSettings().getSoundFXVolume()));
        writer.writeEndElement();

    }

    private static void writeTextSettingsToFile(XMLStreamWriter writer)
            throws XMLStreamException {

        writer.writeStartElement(ST_PLAYER_NAME);
        // TODO writer.writeCharacters(String.valueOf(Quest.getPlayerName()));
        writer.writeEndElement();

    }

    /** Load a game data from a specified XML file.
     * @param file The path of the file name from which the data is loaded.
     */
    public static void loadSettings(String fileName)
            throws XMLStreamException, IOException, ParserConfigurationException, SAXException {

        File file = new File(fileName);

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(file);
        doc.getDocumentElement().normalize();

        if (!doc.getDocumentElement().getNodeName().equals(S_GAME)) {
            return;
        }

        // load the sound settings
        NodeList nodeList = doc.getElementsByTagName(S_SOUNDS);
        if (nodeList.getLength() > 0) {
            initialiseSoundSettings(nodeList.item(0).getChildNodes());
        }

        // load the text settings
        nodeList = doc.getElementsByTagName(S_TEXT);
        if (nodeList.getLength() > 0) {
            initialiseTextSettings(nodeList.item(0).getChildNodes());
        }

    }

    private static void initialiseSoundSettings(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(SM_MUSIC)) {
                    RPG.getSoundSettings().setBackgroundMusic(Boolean.parseBoolean(child.getFirstChild().getNodeValue()));
                } else if (nodeName.equals(SM_MUSIC_VOL)) {
                    RPG.getSoundSettings().setBackgroundMusicVolume(Float.parseFloat(child.getFirstChild().getNodeValue()));
                } else if (nodeName.equals(SM_FX)) {
                    RPG.getSoundSettings().setSoundFX(Boolean.parseBoolean(child.getFirstChild().getNodeValue()));
                } else if (nodeName.equals(SM_FX_VOL)) {
                    RPG.getSoundSettings().setSoundFXVolume(Float.parseFloat(child.getFirstChild().getNodeValue()));
                }
            } else if (child.getNodeType() == Node.ATTRIBUTE_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(SM_MUSIC)) {
                    RPG.getSoundSettings().setPlayingSounds(Boolean.parseBoolean(child.getFirstChild().getNodeValue()));
                }
            }
        }

    }

    private static void initialiseTextSettings(NodeList list) {

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(ST_PLAYER_NAME)) {
                    //TODO Quest.setPlayerName(child.getFirstChild().getNodeValue());
                }
            }
        }

    }

    public static boolean loadSubMenus(Menu menu, String fileName)
            throws XMLStreamException, IOException, ParserConfigurationException, SAXException {

        File file = new File(fileName);

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(file);
        doc.getDocumentElement().normalize();

        if (!doc.getDocumentElement().getNodeName().equals(S_GAME)) {
            return false;
        }

        // load the submenus
        NodeList nodeList = doc.getDocumentElement().getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            Node parent = nodeList.item(j);
            if ((parent.getNodeType() == Node.ELEMENT_NODE) && (parent.getNodeName().equals(S_MENU))) {
                NodeList nodeListMenu = parent.getChildNodes();
                for (int i = 0; i < nodeListMenu.getLength(); i++) {
                    Node child = nodeListMenu.item(i);
                    if ((child.getNodeType() == Node.ELEMENT_NODE) && (child.getNodeName().equals(MENU_CONTENTS))) {
                        if (!readSubMenuFromFile(menu, child.getChildNodes())) {
                            return false;
                        }
                    }
                }
            }
        }

        return true;

    }

    private static boolean readSubMenuFromFile(Menu menu, NodeList list) {

        ArrayList<MenuTab> tabs = new ArrayList<MenuTab>();
        String title = "", type = "", okCancel = "";

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(MENU_TAB)) {
                    MenuTab t = readMenuTabFromFile(menu, child.getChildNodes());
                    if (t != null) {
                        tabs.add(t);
                    }
                } else if (nodeName.equals(MENU_TITLE)) {
                    title = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_TYPE)) {
                    type = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_OK_CANCEL)) {
                    okCancel = child.getFirstChild().getNodeValue();
                }
            }
        }

        try {

            boolean okcan = (okCancel.length() == 0) ? false : Boolean.parseBoolean(okCancel);

            if (type.equals(MENU_ABOUT)) {
                SubMenu subMenu = new SubMenu(menu, tabs, title, okcan);
                if (subMenu != null) {
                    menu.setMenuAbout(subMenu);
                } else {
                    return false;
                }
            } else if (type.equals(MENU_SETTINGS)) {
                SubMenu subMenu = new SubMenu(menu, tabs, title, okcan);
                if (subMenu != null) {
                    menu.setMenuSettings(subMenu);
                } else {
                    return false;
                }
            } else if (type.equals(MENU_HELP)) {
                SubMenu subMenu = new SubMenu(menu, tabs, title, okcan);
                if (subMenu != null) {
                    menu.setMenuHelp(subMenu);
                } else {
                    return false;
                }
            } else if (type.length() == 0) {
                return false;
            }

        } catch (NumberFormatException e) {
            e.printStackTrace();
            return false;
        } catch (SlickException e) {
            e.printStackTrace();
            return false;
        }

        return true;

    }

    private static MenuTab readMenuTabFromFile(Menu menu, NodeList list) {

        MenuTab tab = null;
        ArrayList<MenuTabObject> tabObjects = new ArrayList<MenuTabObject>();
        String title = "";

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(MENU_OBJECT)) {
                    MenuTabObject o = readMenuTabObjectFromFile(menu, child.getChildNodes());
                    if (o != null) {
                        tabObjects.add(o);
                    }
                } else if (nodeName.equals(MENU_TITLE)) {
                    title = child.getFirstChild().getNodeValue();
                }
            }
        }

        try {

            tab = new MenuTab(tabObjects, title);

        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (SlickException e) {
            e.printStackTrace();
        }

        return tab;

    }

    private static MenuTabObject readMenuTabObjectFromFile(Menu menu, NodeList list) {

        MenuTabObject obj = null;
        String pos_x = "", pos_y = "", value = "", type = "", right = "",
                center = "", color = "", min = "", max = "", link = "";

        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getNodeName();
                if (nodeName.equals(S_POS_X)) {
                    pos_x = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_POS_Y)) {
                    pos_y = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_VALUE)) {
                    value = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_RIGHT)) {
                    right = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(S_CENTER)) {
                    center = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_TYPE)) {
                    type = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_COLOR)) {
                    color = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_MIN)) {
                    min = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_MAX)) {
                    max = child.getFirstChild().getNodeValue();
                } else if (nodeName.equals(MENU_LINK)) {
                    link = child.getFirstChild().getNodeValue();
                }
            }
        }

        try {

            if (type.length() == 0) {
                return null;
            }
            boolean rightAligned = (right.length() == 0) ? false : Boolean.parseBoolean(right);
            boolean centerAligned = (center.length() == 0) ? false : Boolean.parseBoolean(center);
            float minimum = (min.length() == 0) ? 0f : Float.parseFloat(min);
            float maximum = (max.length() == 0) ? 100f : Float.parseFloat(max);
            Color clr = FontLoader.getColor(color, Color.black);

            Alignment align = Alignment.LEFT;
            Image img;

            if (rightAligned) {
                align = Alignment.RIGHT;
            } else if (centerAligned) {
                align = Alignment.CENTER;
            }

            if (type.equals("ANIMATION")) {

                img = ImageLoader.getLoadedImage(value);
                Animation ani = new Animation(new SpriteSheet(img.getFlippedCopy(rightAligned, false), img.getWidth(), img.getWidth()), Unit.FRAME_SLOW_INTERVAL);
                obj = new MenuTabAnimation(ani, Float.parseFloat(pos_x), Float.parseFloat(pos_y));

            } else if (type.equals("IMAGE")) {

                img = ImageLoader.getLoadedImage(value);
                obj = new MenuTabImage(img.getFlippedCopy(rightAligned, false), Float.parseFloat(pos_x), Float.parseFloat(pos_y));

            } else if (type.equals("LABELBOX")) {

                obj = new MenuTabLabel(value, Float.parseFloat(pos_x), Float.parseFloat(pos_y), align);

            } else if (type.equals("SLIDER")) {

                Link l = (link.length() == 0) ? Link.NO_LINK : Link.valueOf(link);
                float val = (value.length() == 0) ? 0f : Float.parseFloat(value);
                if (l == Link.MUSIC_VOL) {
                    val = RPG.getSoundSettings().getBackgroundMusicVolume();
                } else if (l == Link.FX_VOL) {
                    val = RPG.getSoundSettings().getSoundFXVolume();
                }

                obj = new MenuTabSlider(minimum, maximum, val, Float.parseFloat(pos_x), Float.parseFloat(pos_y), align, l);

            }

            obj.setColor(clr);

        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (SlickException e) {
            e.printStackTrace();
        }

        return obj;

    }

    public static ArrayList<String> readTextFile(String fileName) {

        ArrayList<String> s = new ArrayList<String>();
        try
        {
            BufferedReader in = new BufferedReader(new FileReader(fileName));
            String str;
            while ((str = in.readLine()) != null)
            {
                s.add(str);
            }
            in.close();
        }
        catch (IOException e)
        { }

        return s;
        
    }
    
}
